# -*- coding: utf-8 -*-

import os
import sys

if __name__ == "__main__":
    currentdir = os.path.dirname( os.path.abspath( sys.argv[0] ) )
    basedir = os.path.abspath( os.path.join( currentdir, ".." ) )
    pythondir = basedir
    sys.path.insert( 0, pythondir )

from jsonrpc.jmsghandler import RequestHandler
from jsonrpc.sendmsg import sendjmsg
from jsonrpc import Server
from time import sleep, time

__all__ = ['Slite3DatabaseHandler', 'Sqlite3DatabaseServer', 'Sqlite3DatabaseManager', 'Sqlite3DatabaseRequest']

#---------------------------------------------

class Sqlite3DatabaseHandler(RequestHandler):
    """
    Do some tasks, maybe in subprocess
    """
    
    def _json_handle(self, json_data):
        """
        @param json_data: simple object data, after json.loads
        @type: mixed
        
        @return: python simple object data, jsonable via json.dumps
        @rtype: mixed
        """
        result = False
        
        if not (type(json_data) is dict):
            return result

        if json_data["task"] == "ping":
            return self.server.ping()
        if json_data["task"] == "quit":
            return self.server.quit()
        if json_data["task"] == "query":
            json_data = json_data["data"]
            return self.server.query( id(json_data), json_data["name"], json_data["data"])
        return result
    
    pass

#---------------------------------------------

class Sqlite3DatabaseServer(Server):

    sql = None

    queue = None
    stop_event = None
    _worker_thread = None

    _query_results = None

    def _worker ( self ):

        print("Sqlite3DatabaseServer: START: %s" % (self.server_address,))

        from project.sqlite3_raw import Project

        p = Project()
        p.connect()

        while not self.stop_event.isSet():

            try:
                data = self.queue.get_nowait()
            except Exception:
                data = None

            if data is None:
                sleep(0.01)
                continue

            request_id, query_name, query_data = data

            try:
                result = getattr(p, query_name)(*query_data)
            except Exception as e:
                result = None
                print("Sqlite3DatabaseServer: ERROR: %s" % e)
                pass

            self._query_results_lock.acquire()
            self._query_results[ request_id ]["result"] = result
            self._query_results[ request_id ]["lock"].release()
            self._query_results_lock.release()

            self.queue.task_done()

        p.close()

        print("Sqlite3DatabaseServer: STOP: %s" % (self.server_address,))

        self.stop_event.clear()
        return


    def server_activate(self):
        from Queue import Queue
        from threading import Thread, Event, Lock

        self._query_results = {}
        self._query_results_lock = Lock()

        self.queue = Queue()

        self.stop_event = Event()
        self._worker_thread = Thread(target=self._worker)
        self._worker_thread.start()

        return Server.server_activate(self)

    def ping(self):
        return "pong"

    def query(self, request_id, name, data):

        from threading import Lock

        self._query_results_lock.acquire()

        lock = Lock()
        self._query_results[ request_id ] = {
            "result" : None,
            "lock" : lock
        }

        self._query_results_lock.release()

        lock.acquire()

        self.queue.put_nowait((request_id, name, data,))

        lock.acquire()
        result = self._query_results[ request_id ]["result"]
        del self._query_results[ request_id ]
        lock.release()

        return result

    def _quit_thread(self):
        sleep(0.05)
        self.server_close()
        return

    def quit(self):
        print("Sqlite3DatabaseServer: quit event")

        from threading import Thread

        th = Thread(target = self._quit_thread)
        th.daemon = True
        th.start()
        return True

    pass

#---------------------------------------------


class Sqlite3DatabaseRequest(object):

    manager = None

    def __init__(self, manager):
        self.manager = manager
        pass

    def ping(self):
        return self.manager.sendmsg("ping", {})

    def query(self, name, data):
        return self.manager.sendmsg("query", {
            "name" : name,
            "data" : data
        })

    def quit(self):
        return self.manager.sendmsg("quit", {})

    pass


#---------------------------------------------

class Sqlite3DatabaseManager(object):

    host = "127.0.1.8"
    port_min = 10000
    port_max = 20000
    port = None
    request = None

    address = None

    def start_service(self):

        import random

        self.port = random.randint(self.port_min, self.port_max)

        self.address = (self.host, self.port,)

        from archive import which
        from subprocess import Popen

        Popen([which("python3"), "service/sqlite3_database.py",
               "--host", "%s" % self.host,
               "--port", "%s" % self.port,
        ])

        # WAIT WHILE SERVICE STARTS

        pings = 10
        runing = False
        t_begin = time()
        while not runing:
            pong = self.sendmsg("ping", {})

            if pong == "pong":
                runing = True

            pings -= 1
            if pings <= 0:
                break

        if pings <= 0:
            print("Sqlite3DatabaseManager: FAIL TO START service on %r in %s sec" % (self.address, time() - t_begin,))
        else:
            print("Sqlite3DatabaseManager: STARTED service on %r in %s sec" % (self.address, time() - t_begin,))

        return runing

    def sendmsg(self, task, data):
        count = 10
        t_wait = 0.1

        result = None
        while count > 0:
            t_begin = time()
            try:
                result = sendjmsg( self.address, {"task":task, "data":data}, timeout=t_wait )
                break
            except Exception:
                result = None
                count -= 1
                t_end = time()
                if t_end - t_begin < t_wait:
                    sleep(t_wait - (t_end - t_begin))
        if count <= 0:
            print("Sqlite3DatabaseManager: FAIL SEND MESSAGE: task=%s, data=%r" % (task, data,))
        return result

    def quit(self):
        return self.sendmsg("quit", {})

    def get_request(self):
        if not self.request:
            self.request = Sqlite3DatabaseRequest(self)
        return self.request

    pass


#---------------------------------------------


def run_service(host, port):

    try:
        SERVER_ADDRESS = (host, int(port),)

        server = Sqlite3DatabaseServer(SERVER_ADDRESS, Sqlite3DatabaseHandler, bind_and_activate=False)

        server.server_bind()
        server.server_activate()
        server.serve_forever()
    except Exception:
        pass

    return

#---------------------------------------------


def launch():

    from argparse import ArgumentParser

    parser = ArgumentParser(description='Run statistics service.')
    parser.add_argument('--host', dest='host', action='store', type=str,
        help='Listen on TCP address')
    parser.add_argument('--port', dest='port', action='store', type=int,
        help='Listen on TCP port')

    args = parser.parse_args()
    run_service(args.host, args.port)

    return 0

if __name__ == "__main__":
    launch()
